#ifndef CLI_H
#define CLI_H
#include "helpfunc.h"
#include "base.h"


class sockMapData
{
public:
    int num;
    vector<char> id;
    vector<uint64_t> askList,dataList;
    uint64_t createTime;
    sockMapData(){}
    sockMapData(int port, vector<char> i, vector<uint64_t> al,
                vector<uint64_t> dl, uint64_t ct) {
        num = port;
        id = i;
        askList = al;
        dataList = dl;
        createTime = ct;
    }
};

class aliveSockV {
public:
    int so;
    int port;
    aliveSockV(){}
    aliveSockV(int s, int p) {
        so = s;
        port = p;
    }
};

class cli:public base
{
public:
    cli( deque<int> d):base(helpFunc::cliPackLen)
    {
        deadSock=d;
        fds  = new  struct pollfd[d.size()+20];
    }
    struct pollfd * fds ;
    deque<int> deadSock;
    deque<aliveSockV> aliveSock;
    map<int,sockMapData> sockMap;
    uint64_t staTime = helpFunc::getTime();
    int staGot = 0, staOut = 0;
    int udpCo = 0;
    uint64_t lastCheckTimeout = 0;
    int newSockThisSecond = 0;
    int recvTime = 0;
    int getNumPerPack = helpFunc::con_getNum;
    uint64_t peerMaxPos = 0;
    map<uint64_t, sendMapV> recvMap;
    int rRaw = 0, wRaw = 0;
    int timeoutTime = helpFunc::timeoutTime;
    int peerMaxGotLen = 0, peerMaxSendLen = 0;
    int nullSend = 0;
    int staSend = 0;
    int newSockLimit = helpFunc::maxNewSockPerSecond;
    int controlSock;
    int controlPort;
    int serverTotalSendLen = helpFunc::serverSenLen;
    int serverOneLen = helpFunc::serverPackLen;
    int totalSend = 0;
    deque<vector<char>> sendL, recvL;
    int pollNum=0;
    vector<char> getId() {
        udpCo++;
        if (udpCo == 60000)
            udpCo = 0;
        mybuf w ;
        w.writeWord(udpCo);

        int st = udpCo % helpFunc::damL.size();
        w.writeArr2(helpFunc::damL.at(st));
        return w.data;
    }

    void dealSta(vector<char> ss) {
        vector<uint64_t> l;
        mybuf w(ss);
        uint64_t mi = w.readQ();
        if (mi > peerPos) {
            int le = 0;
            for (uint64_t i = peerPos; i < mi; i++) {
                le += sendMap.at(i).data.size();
                sendMap.erase(i);
            }
            peerPos = mi;
            tellAddBuf(le);
        }
        uint64_t ma = w.readQ();
        auto d = w.getLeft();
        int co = -1;
        uint8_t b = 0;
        int pos = 0;
        for (uint64_t i = mi; i < ma + 1; i++) {
            co++;
            if (co == 0) {
                b = d[pos];
                pos++;
                if ((b & 128) != 0)
                    l.push_back(i);
                b <<= 1;
            } else {
                if ((b & 128) != 0)
                    l.push_back(i);
                b <<= 1;
                if (co == 7)
                    co = -1;
            }
        }
        for (auto one : l) {
            if (sendMap.count(one))
                sendMap.at(one).got = true;
        }
    }

    void dealRecvPack(int channel) {
        int num = sockMap[channel].num;
        auto id = sockMap[channel].id;
        auto  askList =sockMap[channel].askList;
        auto dataList = sockMap[channel].dataList;
        auto j=  helpFunc::udpRead(channel);
        if(j.size()<=0)
        {
            addToDeadSock(channel);
            return;
        }
        mybuf w (j);
        int lll = w.readByte();
        auto xx = w.readArr2(lll);
        if (xx!= id) {
            addToDeadSock(channel);
            return;
        }
        if (dataList.size() > 0 && sendMap.count(dataList[0]))
            sendMap[dataList[0]].got = true;

        lastRecvTime = helpFunc::getTime();
        staGot += 1;
        rRaw += j.size();
        if (j.size() > maxGotLen)
            maxGotLen = j.size();
        uint64_t tt = helpFunc::getTime() - sockMap[channel].createTime;
        if (tt > recvTime)
            recvTime = (int) tt;
        uint64_t ttt = w.readQ();
        if (ttt > peerMaxPos)
            peerMaxPos = ttt;
        int pmg = w.readWord();
        if (pmg > peerMaxGotLen)
            peerMaxGotLen = pmg;
        pmg = w.readWord();
        if (pmg > peerMaxSendLen)
            peerMaxSendLen = pmg;
        int sendNum = w.readByte();
        for (int i = 0; i < askList.size(); i++) {
            uint64_t nn = askList[i];
            vector<char> st;
            if (i < sendNum) {
                int le = w.readWord();
                st = w.readArr2(le);
            }
            if (!recvMap.count(nn) || recvMap[nn].got)
                continue;
            if (i < sendNum && st.size() > 0) {
                recvMap[nn].got = true;
                recvMap[nn].data = st;
                checkRecvMap();
            } else {
                recvMap[nn].sendTime--;
            }
        }
        int hasSta = w.readByte();
        if (hasSta == 1) {
            int le = w.readWord();
            auto sta = w.readArr2(le);
            dealSta(sta);
        }
        int addPackNum = w.readByte();
        for (int i = 0; i < addPackNum; i++) {
            uint64_t nn = w.readQ();
            int ll = w.readWord();
            auto st = w.readArr2(ll);
            if (nn < recvPos)
                continue;
            sendMapV v;
            v.got = true;
            v.data = st;
            recvMap[nn]= v;
        }
        checkRecvMap();
        sockMap.erase(channel);
        aliveSockV v(channel, num);
        aliveSock.push_back(v);
    }
    void doPeriodWork() {
        if (helpFunc::getTime() - lastRecvTime > 120 * 1000)
            quick_exit(0);
        timeoutTime = recvTime + helpFunc::con_timeoutPlus;
        if (recvTime == 0 || recvTime > helpFunc::timeoutTime)
            timeoutTime = helpFunc::timeoutTime;
        if (staOut == 0)
            gotRate = 1;
        else
            gotRate = staGot / (float) (staGot + staOut);
        maxAskTime = calAskTime();
        qDebug()<<sockMap.size()<<" "<< staGot<<" "<<
                  staOut<<" "<<
                  recvTime<<" "<<endl;

        staGot = staOut = newSockThisSecond = recvTime = staSend = 0;
        rRaw = wRaw = rNet = wNet = 0;
        nullSend = totalSend = 0;
        peerMaxGotLen = peerMaxSendLen = maxGotLen = maxSenLen = 0;
    }
    void sendPacks()  {
        float passTime = (helpFunc::getTime() - staTime) /  1000.0;
        if (passTime > 1)
            passTime = 1;

        int n = (int) (passTime * newSockLimit - newSockThisSecond);
        if (n < 0)
            n = 0;
        if (n > deadSock.size())
            n = deadSock.size();
        n = aliveSock.size() + n;

        float minPackNum, maxSend;
        if (gotRate ==0)
            minPackNum = maxSend = 20000;
        else {
            minPackNum = passTime * (helpFunc::serverStaPerSecond / gotRate);
            maxSend = passTime * (helpFunc::con_maxPackRecv / gotRate);
        }
        int maxSend_int = (int) (maxSend - totalSend);
        if (n > maxSend_int)
            n = maxSend_int;
        int staSendNum = (int) (minPackNum - staSend);
        if (staSendNum < 0)
            staSendNum = 0;
        if (staSendNum > n)
            staSendNum = n;
        staSend += staSendNum;
        auto askList = getAskList(n, staSendNum);
        auto dataList = getDataToSend(n);
        totalSend += n;
        for (int i = 0; i < n; i++) {
            vector<uint64_t> al,dl;
            if (askList.size() > i)
                al = askList.at(i);
            if (dataList.size() > i)
                dl.push_back(dataList.at(i));
            int hasSta = 0;
            if (staSendNum > 0) {
                staSendNum -= 1;
                hasSta = 1;
            } else {
                hasSta = 0;
                if (al.size() == 0 && dl.size() == 0)
                    continue;
            }
            if (aliveSock.size() == 0) {
                int a = deadSock[0];
                deadSock.pop_front();
                int s =  socket (AF_INET, SOCK_DGRAM, 0);
                newSockThisSecond++;
                aliveSockV v(s,a);
                aliveSock.push_back(v);
            }
            packMsg_send(al, dl, hasSta);
            if (al.size() == 0 && dl.size() == 0)
                nullSend++;
        }
    }

    void work()  {
        pollNum=0;
        for (auto channel : sockMap)
        {
            fds[pollNum].fd=channel.first;
            fds[pollNum].events=POLLIN;
            pollNum++;
        }
        rig();
        auto tt=calWaitTime();
        if (tt>1000)
            qDebug()<<"calTime "<<tt<<endl;
        int r=poll(fds, pollNum,tt );
        for (int lo=0;lo<pollNum;lo++)
        {
            auto fd=fds[lo].fd;
            if (fd == dataSock &&fds[lo].revents&POLLIN)
            {
                dealDataRecv();
            }

            else if (fd == dataSock && fds[lo].revents&POLLOUT) {
                auto wn=   helpFunc::tcpWrite(dataSock,wrietBuf.data);
                wrietBuf.data = helpFunc::vecRight(wrietBuf.data,wn);
                wNet += wn;
            }
            else if (fd == numSock &&  fds[lo].revents&POLLOUT) {
                auto wn=   helpFunc::tcpWrite(numSock,wrietBuf2.data);
                wrietBuf2.data = helpFunc::vecRight(wrietBuf2.data,wn);
            }
            if(sockMap.count(fd) &&(fds[lo].revents&POLLIN))
            {
                dealRecvPack( fd);
            }
            reFreshSendMap();
            if (helpFunc::getTime() - lastCheckTimeout > helpFunc::con_timeoutInternal) {
                lastCheckTimeout = helpFunc::getTime() ;
                dealTimeout();
            }
            sendPacks();
            if (helpFunc::getTime() - staTime > 1000) {
                staTime = helpFunc::getTime();
                doPeriodWork();
            }
        }
    }

    void checkRecvMap() {
        while (recvMap.count(recvPos)
               && recvMap.at(recvPos).got) {
            wrietBuf.writeArr2(recvMap.at(recvPos).data);
            recvMap.erase(recvPos);
            recvPos++;
        }
    }
    void packMsg_send(vector<uint64_t> askList, vector<uint64_t>  dataList, int hasSta)
    {
        std::sort(askList.begin(),askList.end());
        uint64_t tt = helpFunc::getTime();
        aliveSockV v = aliveSock[0];
        aliveSock.pop_front();
        mybuf w ;
        w.writeArr2(key);
        auto  id = getId();
        w.writeByte(id.size());
        w.writeArr2(id);
        w.writeQ(recvPos);
        w.writeFloat(gotRate);
        if (dataList.size() == 0)
            w.writeByte(0);
        else {
            w.writeByte(1);
            uint64_t dd = dataList.at(0);
            w.writeQ(dd);
            w.writeWord(sendMap.at(dd).data.size());
            w.writeArr2(sendMap.at(dd).data);
        }
        int le = askList.size();
        w.writeByte(hasSta);
        w.writeByte(le);
        if (le != 0) {
            w.writeQ(askList.at(0));
            for (int i = 0; i < le - 1; i++) {
                w.writeDWord((int) (askList.at(i + 1) - askList.at(0)));
            }
        }
        sockMapData vv (v.port, id, askList, dataList, tt);
        helpFunc::udpWrite(v.so,w.data,helpFunc::serverIp,v.port,true);
        sockMap[v.so]= vv;
        wRaw += w.data.size();
        if (w.data.size() > maxSenLen)
            maxSenLen = w.data.size();
    }

    vector<vector<uint64_t>> getAskList(int n, int staPackNum) {
        vector<vector<uint64_t>> l;
        if (n == 0)
            return l;
        int staLen = (int) (17 + (sendPos - peerPos) / 8);
        int sendNum = (serverTotalSendLen - 20 - helpFunc::damMaxLen- staLen) / (serverOneLen + 2);
        if (sendNum <= 0)
            sendNum = 0;

        vector<uint64_t> tempList;
        int thisLoopSendTime = 0;
        while (true) {
            thisLoopSendTime++;
            if (thisLoopSendTime > maxAskTime) {
                if (tempList.size() != 0 && l.size() < n)
                    l.push_back(tempList);
                return l;
            }
            for (uint64_t i = recvPos; i < peerMaxPos; i++) {
                if (staPackNum > 0 && tempList.size() >= sendNum) {
                    staPackNum -= 1;
                    l.push_back(tempList);
                    tempList.clear();
                    if (l.size() == n)
                        return l;
                }
                if (!recvMap.count(i))
                    recvMap[i]= sendMapV();
                if (!recvMap.at(i).got && recvMap.at(i).sendTime < thisLoopSendTime) {
                    if(vContains(tempList,i))
                    {
                        l.push_back(tempList);
                        tempList.clear();
                        if (l.size() == n)
                            return l;
                    }
                    recvMap.at(i).sendTime++;
                    tempList.push_back(i);
                    if (tempList.size() == getNumPerPack) {
                        l.push_back(tempList);
                        tempList.clear();
                        if (l.size() == n)
                            return l;
                    }
                }
            }
        }
    }
    bool vContains(vector<uint64_t> &v,uint64_t i)
    {
        for(auto x:v)
        {
            if (x==i)
                return true;
        }
        return false;
    }

    vector<uint64_t> getDataToSend(int n) {
        vector<uint64_t>  l ;
        if (n == 0)
            return l;
        int thisLoopSendTime = 0;
        while (true) {
            thisLoopSendTime++;
            if (thisLoopSendTime > maxAskTime)
                return l;
            for (uint64_t i = peerPos; i < sendPos; i++) {
                if (!sendMap.at(i).got && sendMap.at(i).sendTime < thisLoopSendTime) {
                    sendMap.at(i).sendTime++;
                    l.push_back(i);
                    if (l.size() == n)
                        return l;
                }
            }
        }
    }

    void addToDeadSock(int channel){
        int num = sockMap[channel].num;
        vector<uint64_t> askList= sockMap[channel].askList;
        vector<uint64_t> dataList= sockMap[channel].dataList;

        for (auto i : askList) {
            if (recvMap.count(i) && !recvMap[i].got)
                recvMap[i].sendTime--;
        }
        if (dataList.size() > 0) {
            uint64_t dd = dataList[0];
            if (sendMap.count(dd) && !sendMap[dd].got)
                sendMap[dd].sendTime--;
        }
        sockMap.erase(channel);
        staOut++;
        close(channel);
        deadSock.push_back(num);
    }

    void dealTimeout() {
        uint64_t tt = helpFunc::getTime();
        vector<int>temp;
        for(auto i : sockMap)
            temp.push_back(i.first);

        for (auto k : temp) {
            if (sockMap[k].createTime + timeoutTime < tt)
            {
                addToDeadSock(k);
            }

        }
    }
    void rig()  {
        if (wrietBuf.size() > 0)
        {
            fds[pollNum].fd=dataSock;
            fds[pollNum].events=POLLIN|POLLOUT;
            pollNum++;
        }

        else {
            fds[pollNum].fd=dataSock;
            fds[pollNum].events=POLLIN;
            pollNum++;
        }

        if (wrietBuf2.size() > 0)
        {
            fds[pollNum].fd=numSock;
            fds[pollNum].events=POLLOUT;
            pollNum++;
        }
    }

};

#endif // CLI_H
